package com.pt25.base.dep.es;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.aggregations.MultiBucketBase;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.pt25.base.constants.BaseConstants;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class EsService {
    @Resource(name = "elasticsearchClientMap")
    private Map<String, ElasticsearchClient> elasticsearchClientMap;

    public ElasticsearchClient getElasticsearchClient() {
        return elasticsearchClientMap.get(BaseConstants.DEFAULT);
    }

    public ElasticsearchClient getElasticsearchClient(String name) {
        return elasticsearchClientMap.get(name);
    }

    // 创建索引
    public void createIndex(String clusterName, String indexName) throws IOException {
        getElasticsearchClient(clusterName).indices().create(c -> c.index(indexName));
    }

    // 删除索引
    public void deleteIndex(String clusterName, String indexName) throws IOException {
        getElasticsearchClient(clusterName).indices().delete(c -> c.index(indexName));
    }

    // 索引是否存在
    public boolean existIndex(String clusterName, String indexName) throws IOException {
        return getElasticsearchClient(clusterName).indices()
                .exists(c -> c.index(indexName))
                .value();
    }


    // 添加文档
    public <T> void indexDocument(String clusterName, String indexName, String id, T document) throws IOException {
        getElasticsearchClient(clusterName).index(i -> i
                .index(indexName)
                .id(id)
                .document(document)
        );
    }

    // 获取文档
    public <T> T getDocument(String clusterName, String indexName, String id, Class<T> clazz) throws IOException {
        return getElasticsearchClient(clusterName).get(g -> g
                        .index(indexName)
                        .id(id),
                clazz
        ).source();
    }

    // 更新文档
    public <T> void updateDocument(String clusterName, String indexName, String id, T document) throws IOException {
        getElasticsearchClient(clusterName).update(u -> u
                        .index(indexName)
                        .id(id)
                        .doc(document),
                Object.class
        );
    }

    // 删除文档
    public void deleteDocument(String clusterName, String indexName, String id) throws IOException {
        getElasticsearchClient(clusterName).delete(d -> d
                .index(indexName)
                .id(id)
        );
    }

    // 批量插入
    public <T> void bulkIndex(String clusterName, String indexName, Map<String, T> documents) throws IOException {
        BulkRequest.Builder builder = new BulkRequest.Builder();

        documents.forEach((id, doc) ->
                builder.operations(op -> op
                        .index(idx -> idx
                                .index(indexName)
                                .id(id)
                                .document(doc)
                        )
                )
        );

        getElasticsearchClient(clusterName).bulk(builder.build());
    }

    // 批量删除
    public void bulkDelete(String clusterName, String indexName, List<String> ids) throws IOException {
        BulkRequest.Builder builder = new BulkRequest.Builder();

        ids.forEach(id ->
                builder.operations(op -> op
                        .delete(d -> d
                                .index(indexName)
                                .id(id)
                        )
                )
        );

        getElasticsearchClient(clusterName).bulk(builder.build());
    }

    // 简单查询
    public <T> List<T> search(String clusterName, String indexName, String field, String value, Class<T> clazz) throws IOException {
        return getElasticsearchClient(clusterName).search(s -> s
                                .index(indexName)
                                .query(q -> q
                                        .match(m -> m
                                                .field(field)
                                                .query(value)
                                        )
                                ),
                        clazz
                ).hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList());
    }

    // 时间范围查询
    public <T> List<T> searchByDateRange(String clusterName, String indexName,
                                         String dateField, Instant start, Instant end, Class<T> clazz) throws IOException {
        return getElasticsearchClient(clusterName).search(s -> s
                                .index(indexName)
                                .query(q -> q
                                        .range(r -> r
                                                .field(dateField)
                                                .gte(JsonData.of(start.toString()))
                                                .lte(JsonData.of(end.toString()))
                                        )
                                ),
                        clazz
                ).hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList());
    }


    // 创建索引并指定映射
    public void createIndexWithMapping(String clusterName, String indexName,
                                       Map<String, Property> properties) throws IOException {
        getElasticsearchClient(clusterName).indices().create(c -> c
                .index(indexName)
                .mappings(m -> m
                        .properties(properties)
                )
        );
    }

    // 示例：创建带有特定映射的索引
    public void createProductIndex(String clusterName) throws IOException {
        Map<String, Property> properties = new HashMap<>();
        properties.put("name", Property.of(p -> p.text(t -> t)));
        properties.put("price", Property.of(p -> p.double_(d -> d)));
        properties.put("createdAt", Property.of(p -> p.date(d -> d)));

        createIndexWithMapping(clusterName, "products", properties);
    }

    // 按字段聚合
    public Map<String, Long> aggregateByField(String clusterName, String indexName,
                                              String fieldName) throws IOException {
        SearchResponse<Void> response = getElasticsearchClient(clusterName).search(s -> s
                        .index(indexName)
                        .size(0)
                        .aggregations(fieldName, a -> a
                                .terms(t -> t.field(fieldName))
                        ),
                Void.class
        );

        return response.aggregations()
                .get(fieldName)
                .sterms()
                .buckets()
                .array()
                .stream()
                .collect(Collectors.toMap(
                        b -> b.key().stringValue(),
                        MultiBucketBase::docCount
                ));
    }

    // 分页查询
    public <T> SearchResult<T> searchWithPagination(String clusterName, String indexName,
                                                    Query query, int page, int size, Class<T> clazz) throws IOException {
        SearchResponse<T> response = getElasticsearchClient(clusterName).search(s -> s
                        .index(indexName)
                        .query(query)
                        .from((page - 1) * size)
                        .size(size),
                clazz
        );

        long totalHits = response.hits().total() != null ?
                response.hits().total().value() : 0;

        return new SearchResult<>(
                response.hits().hits().stream()
                        .map(Hit::source)
                        .collect(Collectors.toList()),
                totalHits,
                page,
                size
        );
    }

    // 分页结果封装类
    @Data
    @AllArgsConstructor
    public static class SearchResult<T> {
        private List<T> content;
        private long totalElements;
        private int currentPage;
        private int pageSize;

        public int getTotalPages() {
            return (int) Math.ceil((double) totalElements / pageSize);
        }
    }

    /**
     * 执行复杂布尔查询(带分页信息)
     *
     * @param clusterName    集群名称
     * @param indexName      索引名称
     * @param mustQueries    must条件列表
     * @param shouldQueries  should条件列表
     * @param mustNotQueries must_not条件列表
     * @param filterQueries  filter条件列表
     * @param from           开始索引
     * @param size           返回数量
     * @param clazz          返回结果类型
     * @param <T>            泛型类型
     * @return 查询结果列表
     */
    public <T> SearchResponse<T> executeBoolQueryWithPaging(
            String clusterName,
            String indexName,
            List<Query> mustQueries,
            List<Query> shouldQueries,
            List<Query> mustNotQueries,
            List<Query> filterQueries,
            int from,
            int size,
            Class<T> clazz) throws IOException {

        return getElasticsearchClient(clusterName).search(s -> s
                        .index(indexName)
                        .from(from)
                        .size(size)
                        .query(q -> q
                                .bool(b -> {
                                    b.must(mustQueries);
                                    b.should(shouldQueries);
                                    b.mustNot(mustNotQueries);
                                    b.filter(filterQueries);
                                    return b;
                                })
                        ),
                clazz
        );
    }

    /**
     * 执行复杂布尔查询
     *
     * @param clusterName    集群名称
     * @param indexName      索引名称
     * @param mustQueries    must条件列表
     * @param shouldQueries  should条件列表
     * @param mustNotQueries must_not条件列表
     * @param filterQueries  filter条件列表
     * @param clazz          返回结果类型
     * @param <T>            泛型类型
     * @return 查询结果列表
     */
    public <T> List<T> executeBoolQuery(
            String clusterName,
            String indexName,
            List<Query> mustQueries,
            List<Query> shouldQueries,
            List<Query> mustNotQueries,
            List<Query> filterQueries,
            Class<T> clazz) throws IOException {

        SearchResponse<T> response = getElasticsearchClient(clusterName).search(s -> s
                        .index(indexName)
                        .query(q -> q
                                .bool(b -> {
                                    b.must(mustQueries);
                                    b.should(shouldQueries);
                                    b.mustNot(mustNotQueries);
                                    b.filter(filterQueries);
                                    return b;
                                })
                        ),
                clazz
        );

        return response.hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList());
    }

    /**
     * 构建匹配查询
     */
    public Query buildMatchQuery(String field, Object value) {
        return Query.of(q -> q
                .match(m -> m
                        .field(field)
                        .query(convertToFieldValue(value))
                )
        );
    }

    /**
     * 构建范围查询
     */
    public Query buildRangeQuery(String field, Object gt, Object lt) {
        return Query.of(q -> q
                .range(r -> {
                    if (gt != null) {
                        r.gt(JsonData.of(gt));
                    }
                    if (lt != null) {
                        r.lt(JsonData.of(lt));
                    }
                    return r.field(field);
                })
        );
    }

    /**
     * 构建多字段匹配查询
     */
    public  Query buildMultiMatchQuery(String value, String... fields) {
        return Query.of(q -> q
                .multiMatch(m -> m
                        .query(value)
                        .fields(Arrays.asList(fields))
                )
        );
    }

    /**
     * 构建嵌套查询
     */
    public Query buildNestedQuery(String path, Query query) {
        return Query.of(q -> q
                .nested(n -> n
                        .path(path)
                        .query(query)
                )
        );
    }

    /**
     * 构建完整的布尔查询构建器
     */
    public Function<BoolQuery.Builder, BoolQuery.Builder> buildBoolQueryBuilder(
            List<Query> mustQueries,
            List<Query> shouldQueries,
            List<Query> mustNotQueries,
            List<Query> filterQueries) {

        return builder -> {
            if (mustQueries != null && !mustQueries.isEmpty()) {
                builder.must(mustQueries);
            }
            if (shouldQueries != null && !shouldQueries.isEmpty()) {
                builder.should(shouldQueries);
            }
            if (mustNotQueries != null && !mustNotQueries.isEmpty()) {
                builder.mustNot(mustNotQueries);
            }
            if (filterQueries != null && !filterQueries.isEmpty()) {
                builder.filter(filterQueries);
            }
            return builder;
        };
    }

    /**
     * 通用对象到FieldValue的转换
     */
    private  FieldValue convertToFieldValue(Object value) {
        if (value == null) {
            throw new IllegalArgumentException("Field value cannot be null");
        }
        // 按类型处理
        if (value instanceof String) {
            return FieldValue.of((String) value);
        } else if (value instanceof Number) {
            if (value instanceof Integer || value instanceof Long) {
                return FieldValue.of(((Number) value).longValue());
            } else if (value instanceof Float || value instanceof Double) {
                return FieldValue.of(((Number) value).doubleValue());
            }
        } else if (value instanceof Boolean) {
            return FieldValue.of((Boolean) value);
        } else if (value instanceof Date) {
            return FieldValue.of(String.valueOf(Instant.ofEpochMilli(((Date) value).getTime())));
        } else if (value instanceof LocalDateTime) {
            return FieldValue.of(String.valueOf(((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant()));
        }
        // 默认转为字符串处理
        return FieldValue.of(value.toString());
    }
}
